using Caliburn.Micro;

namespace SS.WPF.Framework.Markup
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;

    public enum DirtyAction
    {
        Add,
        Remove
    }

    /// <summary>
    /// Confirm Close Markup
    /// </summary>
    public static class DirtyChecker
    {
        #region Constants and Fields

        #region #region DirtyGroupList Attached Property

        public static readonly DependencyProperty DirtyGroupListProperty =
            DependencyProperty.RegisterAttached(
                "DirtyGroupList", typeof(IEnumerable), typeof(DirtyChecker), new PropertyMetadata(OnDirtyGroupListChanged));

        public static IEnumerable GetDirtyGroupList(DependencyObject control)
        {
            return (IEnumerable)control.GetValue(DirtyGroupListProperty);
        }

        public static void SetDirtyGroupList(DependencyObject control, IEnumerable value)
        {
            control.SetValue(DirtyGroupListProperty, value);
        }

        private static void OnDirtyGroupListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        #endregion

        #region DirtyGroup Attached Property

        public static readonly DependencyProperty DirtyGroupProperty =
            DependencyProperty.RegisterAttached(
                "DirtyGroup", typeof(string), typeof(DirtyChecker), new FrameworkPropertyMetadata(OnDirtyGroupChanged));

        public static string GetDirtyGroup(DependencyObject control)
        {
            return (string)control.GetValue(DirtyGroupProperty);
        }

        public static void SetDirtyGroup(DependencyObject control, string value)
        {
            control.SetValue(DirtyGroupProperty, value);
        }

        private static void OnDirtyGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        #endregion

        #region DirtyGroup Attached Property
        
        public static readonly DependencyProperty SuspendCheckingProperty =
            DependencyProperty.RegisterAttached(
                "SuspendChecking", typeof(bool), typeof(DirtyChecker),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits, OnSuspendCheckingChanged));

        public static bool GetSuppendChecking(DependencyObject control)
        {
            return (bool)control.GetValue(SuspendCheckingProperty);
        }

        public static void SetSuppendChecking(DependencyObject control, bool value)
        {
            control.SetValue(SuspendCheckingProperty, value);
        }

        private static void OnSuspendCheckingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        #endregion

        #region IsDirtyCheck Attached Property

        /// <summary>
        ///   A dependency property for preventing closing
        /// </summary>
        public static readonly DependencyProperty IsDirtyCheckProperty =
            DependencyProperty.RegisterAttached(
                "IsDirtyCheck", typeof(bool), typeof(DirtyChecker), new PropertyMetadata(IsDirtyCheckChanged));

        public static bool GetIsDirtyCheck(DependencyObject dependencyObject)
        {
            return (bool)dependencyObject.GetValue(IsDirtyCheckProperty);
        }

        public static void SetIsDirtyCheck(DependencyObject dependencyObject, bool value)
        {
            dependencyObject.SetValue(IsDirtyCheckProperty, value);
        }

        private static void IsDirtyCheckChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var frameworkElement = (FrameworkElement)d;
            var checkDirty = (bool)e.NewValue;
            if (checkDirty)
            {
                if (frameworkElement.DataContext != null)
                {
                    Debug.WriteLine("frameworkElement.DataContext != null - " + frameworkElement);
                    AddNotifyDirtyChangedHandler(frameworkElement);
                }

                frameworkElement.DataContextChanged += OnDataContextChanged;
                if (!frameworkElement.IsLoaded)
                {
                    frameworkElement.Loaded += OnUserControlLoaded;
                }
                else
                {
                    ReinitializeOriginalData(frameworkElement);
                }

                AddRecheckDirtyHandler(frameworkElement, OnRecheckDirty);
            }
            else
            {
                frameworkElement.DataContextChanged -= OnDataContextChanged;
                frameworkElement.Loaded -= OnUserControlLoaded;
                RemoveRecheckDirtyHandler(frameworkElement, OnRecheckDirty);
            }
        }

        private static void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            Debug.WriteLine("OnDataContextChanged " + sender);
            AddNotifyDirtyChangedHandler(sender as FrameworkElement);
        }

        private static void OnRecheckDirty(object sender, RecheckDirtyRoutedEventArgs args)
        {
            var frameworkElement = (FrameworkElement)sender;
            var dirtyGroups = new List<string>();
            var isdirty = CheckDirty(frameworkElement, dirtyGroups);
            SetIsDirty(frameworkElement, isdirty);
            SetDirtyGroupList(frameworkElement, dirtyGroups);
        }

        /// <summary>
        /// Add handler (DirtyChangeChangedHandler) to handle event RecheckDirtyEvent
        /// </summary>
        /// <param name="sender"></param>
        public static void AddNotifyDirtyChangedHandler(FrameworkElement sender)
        {
            if (sender == null)
            {
                return;
            }

            var dataContext = sender.DataContext;
            var dirtyChangeChangedHandler = GetDirtyChangeChangedHandler(sender);
            if (dirtyChangeChangedHandler == null)
            {
                // Add DirtyChangeChangedHandler for Consumer for using NotifyDirtyHandler method
                dirtyChangeChangedHandler = new DirtyChangeChangedHandler(sender);
                SetDirtyChangeChangedHandler(sender, dirtyChangeChangedHandler);
            }

            var notifier = dataContext as INotifyDirtyChanged;
            if (notifier != null)
            {
                notifier.NotifyDirtyChanged += dirtyChangeChangedHandler.NotifyDirtyHandler;
            }
        }

        private static void OnUserControlLoaded(object sender, RoutedEventArgs e)
        {
            var frameworkElement = sender as FrameworkElement;
            ReinitializeOriginalData(frameworkElement);
        }

        private static void ReinitializeOriginalData(FrameworkElement frameworkElement)
        {
            Execute.OnUIThread(() =>
            {
                if (frameworkElement != null)
                {
                    InitializeOriginalData(frameworkElement);
                    RaiseRecheckDirtyEvent(frameworkElement);
                    frameworkElement.Loaded -= OnUserControlLoaded;
                }
            });
        }

        #endregion

        #region IsDirty Attached Property

        /// <summary>
        /// The is dirty property.
        /// </summary>
        public static readonly DependencyProperty IsDirtyProperty = DependencyProperty.RegisterAttached(
            "IsDirty", typeof(bool), typeof(DirtyChecker), new PropertyMetadata(false, IsDirtyChanged));

        public static bool GetIsDirty(DependencyObject dependencyObject)
        {
            return (bool)dependencyObject.GetValue(IsDirtyProperty);
        }

        public static void SetIsDirty(DependencyObject dependencyObject, bool value)
        {
            dependencyObject.SetValue(IsDirtyProperty, value);
        }

        private static void IsDirtyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region OriginalData Attached Property

        /// <summary>
        /// The orgirinal data property.
        /// </summary>
        public static readonly DependencyProperty OriginalDataProperty =
            DependencyProperty.RegisterAttached(
                "OriginalData", typeof(object), typeof(DirtyChecker), new PropertyMetadata(UnsetOriginalData, OrgirinalDataChanged));

        public static object GetOriginalData(DependencyObject dependencyObject)
        {
            return dependencyObject.GetValue(OriginalDataProperty);
        }

        public static void SetOriginalData(DependencyObject dependencyObject, object value)
        {
            dependencyObject.SetValue(OriginalDataProperty, value);
        }

        private static void OrgirinalDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region Ignore Attached Property

        public static readonly DependencyProperty IgnoreProperty =
            DependencyProperty.RegisterAttached(
                "Ignore", typeof(bool), typeof(DirtyChecker), new PropertyMetadata(OnIgnoreChanged));

        private static void OnIgnoreChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        public static bool GetIgnore(DependencyObject control)
        {
            return (bool)control.GetValue(IgnoreProperty);
        }

        public static void SetIgnore(DependencyObject control, bool value)
        {
            control.SetValue(IgnoreProperty, value);
        }

        #endregion

        #region RecheckDirty Attached Property

        /// <summary>
        /// The preview dirty check event.
        /// <para>
        /// Using Buble for the purpose: when Event is raised from source (DirtyChecker),
        /// It will be transmited to container control in VisualTree
        /// </para>
        /// </summary>
        public static readonly RoutedEvent RecheckDirtyEvent = EventManager.RegisterRoutedEvent(
            "RecheckDirty", RoutingStrategy.Bubble, typeof(RecheckDirtyHandler), typeof(DirtyChecker));

        /// <summary>
        /// Register to handle <see cref="RecheckDirtyEvent"/> for a Element in VisualTree 
        /// </summary>
        /// <param name="d">The element in VisualTree which want to register for handling RoutedEvent</param>
        /// <param name="handler">Refer to the Handler</param>
        public static void AddRecheckDirtyHandler(DependencyObject d, RecheckDirtyHandler handler)
        {
            ((UIElement)d).AddHandler(RecheckDirtyEvent, handler);
        }

        /// <summary>
        /// Unregister handle RoutedEvent RecheckDirtyEvent for a Element in VisualTree 
        /// </summary>
        /// <param name="d">The element in VisualTree which want to unregister for handling RoutedEvent</param>
        /// <param name="handler">Refer to the Handler</param>
        public static void RemoveRecheckDirtyHandler(DependencyObject d, RecheckDirtyHandler handler)
        {
            ((UIElement)d).RemoveHandler(RecheckDirtyEvent, handler);
        }

        #endregion

        #region DirtyChanged Attached Property

        /// <summary>
        /// The preview dirty check event.
        /// </summary>
        public static readonly RoutedEvent DirtyChangedEvent = EventManager.RegisterRoutedEvent(
            "DirtyChanged", RoutingStrategy.Bubble, typeof(DirtyChangedHandler), typeof(DirtyChecker));

        public static void AddDirtyChangedHandler(DependencyObject d, DirtyChangedHandler handler)
        {
            ((UIElement)d).AddHandler(DirtyChangedEvent, handler);
        }

        public static void RemoveDirtyChangedHandler(DependencyObject d, DirtyChangedHandler handler)
        {
            ((UIElement)d).RemoveHandler(DirtyChangedEvent, handler);
        }

        #endregion

        #region DirtyChangeChangedHandler Attached Property

        public static readonly DependencyProperty DirtyChangeChangedHandlerProperty =
            DependencyProperty.RegisterAttached(
                "DirtyChangeChangedHandler", typeof(DirtyChangeChangedHandler), typeof(DirtyChecker), new PropertyMetadata(OnDirtyChangeChangedHandlerChanged));

        private static void OnDirtyChangeChangedHandlerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }

        public static DirtyChangeChangedHandler GetDirtyChangeChangedHandler(DependencyObject control)
        {
            return (DirtyChangeChangedHandler)control.GetValue(DirtyChangeChangedHandlerProperty);
        }

        public static void SetDirtyChangeChangedHandler(DependencyObject control, DirtyChangeChangedHandler value)
        {
            control.SetValue(DirtyChangeChangedHandlerProperty, value);
        }

        #endregion

        #region Checker Attached Property

        public static readonly DependencyProperty CheckerProperty =
            DependencyProperty.RegisterAttached(
                "Checker", typeof(IChecker), typeof(DirtyChecker), new PropertyMetadata(OnDirtyCheckerChanged));

        private static void OnDirtyCheckerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AddNotifyDirtyChangedHandler(d as FrameworkElement);
        }

        public static IChecker GetChecker(DependencyObject control)
        {
            return control.GetValue(CheckerProperty) as IChecker;
        }

        public static void SetChecker(DependencyObject control, IChecker value)
        {
            control.SetValue(CheckerProperty, value);
        }

        #endregion

        [DebuggerDisplay("#UnsetOriginalData#")]
        public static readonly object UnsetOriginalData = new object();

        /// <summary>
        /// The handlers.
        /// </summary>
        private static readonly IDictionary<Type, GetValueHashCode> Handlers;

        private static readonly IDictionary<Type, Action<object>> DataChangedHandlers;

        private static readonly IDictionary<Type, Action<object>> SetterHandlers;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes static members of the <see cref="DirtyChecker"/> class.
        /// </summary>
        static DirtyChecker()
        {
            Handlers = new Dictionary<Type, GetValueHashCode>
                {
                    { typeof(TextBox), x => ((TextBox)x).Text },
                    {
                        typeof(ComboBox), x =>
                            {
                                if (((ComboBox)x).SelectedItem == null)
                                {
                                    return string.Empty;
                                }

                                return ((ComboBox)x).SelectedItem;
                            }
                        },
                    {
                        typeof(TextBlock), //x => ((TextBlock)x).Text 
                        x => string.Empty
                    }, 
                    { typeof(Button), x => string.Empty }, 
                    { typeof(CheckBox), x => ((CheckBox)x).IsChecked }, 
                    { typeof(RadioButton), x => ((RadioButton)x).IsChecked }, 
                };

            SetterHandlers = new Dictionary<Type, Action<object>> {
                    { typeof(TextBox), x =>
                        {
                            var textBox = ((TextBox)x);
                            var originalData = GetOriginalData(textBox);
                            if (originalData != null && originalData != UnsetOriginalData)
                            {
                                textBox.Text = originalData.ToString();
                            }
                        } }, 
                    { typeof(ComboBox), x =>
                        {
                            var combobox = ((ComboBox)x);
                            var originalData = GetOriginalData(combobox);
                            if (originalData != null && originalData != UnsetOriginalData)
                            {                                
                                combobox.SelectedItem = originalData;
                                if(combobox.SelectedItem != null)
                                {
                                    var type = combobox.SelectedItem.GetType();
                                    var type1 = originalData.GetType();
                                    if (type != type1
                                        && !combobox.SelectedItem.Equals(originalData))
                                    {
                                        combobox.SelectedValue = originalData;
                                    }
                                }
                            }
                            //else
                            //{
                            //    SetOriginalData(combobox, combobox.SelectedValue);
                            //    combobox.SelectedItem = combobox.Items[0];
                            //}
                        } }, 
                    { typeof(TextBlock), x =>
                        {
                            //var textBlock = ((TextBlock)x);
                            //if (GetOriginalData(textBlock) != null)
                            //{
                            //    textBlock.Text = GetOriginalData(textBlock) as string;
                            //}
                        } }, 
                    { typeof(Button), x => { } }, 
                    { typeof(CheckBox), x =>
                        {
                            try
                            {
                                var checkBox = ((CheckBox)x);
                                checkBox.IsChecked = (bool)GetOriginalData(checkBox);
                            }
                            catch (Exception ex)
                            {
                                Debug.Write(ex);
                            }
                        } 
                        }, 
                     { typeof(RadioButton), x =>
                        {
                            var checkBox = ((RadioButton)x);
                            checkBox.IsChecked = (bool)GetOriginalData(checkBox);
                            
                        } 
                        }, 
                };

            DataChangedHandlers = new Dictionary<Type, Action<object>> {
                    { typeof(TextBox), x=>
                        {
                            var textbox = (TextBox)x;
                            textbox.TextChanged -= OnTextChanged;
                            textbox.TextChanged += OnTextChanged;
                        
                        }
                     
                    }, 
                    { typeof(ComboBox), x => { 
                        var comboBox = (ComboBox)x;
                        comboBox.SelectionChanged -= OnSelectionChanged;
                        comboBox.SelectionChanged += OnSelectionChanged;
                    } }, 
                    { typeof(TextBlock), x => {} }, 
                    { typeof(Button), x => {} }, 
                    { typeof(CheckBox), x =>
                        {
                            var comboBox = (CheckBox)x;
                            comboBox.Checked -= OnSelectionChanged;
                            comboBox.Checked += OnSelectionChanged;
                            comboBox.Unchecked -= OnSelectionChanged;
                            comboBox.Unchecked += OnSelectionChanged;
                        }
                    }, 
                    { 
                        typeof(RadioButton), x =>
                        {
                            var comboBox = (RadioButton)x;
                            comboBox.Checked -= OnSelectionChanged;
                            comboBox.Checked += OnSelectionChanged;
                            comboBox.Unchecked -= OnSelectionChanged;
                            comboBox.Unchecked += OnSelectionChanged;
                        }
                    }, 
                };
        }

        private static void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            OnSelectionChanged(sender, e);
        }

        private static void OnSelectionChanged(object sender, RoutedEventArgs e)
        {
            OnDataChanged(sender);
        }

        private static void OnDataChanged(object sender)
        {
            GetValueHashCode getValue;
            if (Handlers.TryGetValue(sender.GetType(), out getValue))
            {
                var frameworkElement = (FrameworkElement)sender;
                var previousState = GetIsDirty(frameworkElement);
                var text = GetOriginalData(frameworkElement);
                if (ReferenceEquals(UnsetOriginalData, text))
                {
                    SetIsDirty(frameworkElement, false);
                    return;
                }

                var currentValue = getValue(sender);
                var isdirty = (!text.Equals(currentValue));
                SetIsDirty(frameworkElement, isdirty);

                RaiseRecheckDirtyEvent(frameworkElement);

                var group = GetDirtyGroup(frameworkElement);

                if (previousState != isdirty)
                {
                    var action = previousState == false ? DirtyAction.Add : DirtyAction.Remove;
                    frameworkElement.RaiseEvent(new DirtyChangedRoutedEventArgs(DirtyChangedEvent, group,
                        sender.GetHashCode().ToString(), action));
                }
            }
        }

        private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            OnDataChanged(sender);
        }

        public static void RaiseRecheckDirtyEvent(FrameworkElement textbox)
        {
            Debug.WriteLine("RaiseRecheckDirtyEvent for " + textbox);
            var previewDirtyCheckArg = new RecheckDirtyRoutedEventArgs(RecheckDirtyEvent);
            textbox.RaiseEvent(previewDirtyCheckArg);
        }

        #endregion

        #region Delegates

        /// <summary>
        /// The get value hash code.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        public delegate object GetValueHashCode(object obj);

        #endregion

        #region Public Methods

        /// <summary>
        /// Check is dirty.
        /// </summary>
        /// <param name="element">The DependencyObject.</param>
        /// <param name="dirtyGroups"></param>
        /// <returns>
        /// True if the window is dirty.
        /// </returns>
        public static bool CheckDirty(DependencyObject element, IList<string> dirtyGroups)
        {
            var isDirty = false;
            if (element is Visual || element is Visual3D)
            {
                var frameworkElement = (FrameworkElement)element;

                if (GetIgnore(frameworkElement))
                {
                    return false;
                }

                var checker = GetChecker(element);
                if (checker != null)
                {
                    var dirty = checker.CheckDirty(element, frameworkElement.DataContext);
                    SetIsDirty(frameworkElement, dirty);
                    if (dirty)
                    {
                        AddToDirtyGroup(element, dirtyGroups);
                    }
                    if (checker.Handled || dirty)
                    {
                        return dirty;
                    }
                }

                var noVisual = VisualTreeHelper.GetChildrenCount(element);
                for (int i = 0; i < noVisual; i++)
                {
                    var child = VisualTreeHelper.GetChild(element, i);

                    if (child == null)
                    {
                        continue;
                    }

                    if (GetIgnore(child))
                    {
                        continue;
                    }

                    GetValueHashCode hashCode;
                    if (Handlers.TryGetValue(child.GetType(), out hashCode))
                    {
                        var dependencyProperty = Control.GetDependencyProperty(child);
                        if (dependencyProperty != null)
                        {
                            if (!BindingOperations.IsDataBound(child, dependencyProperty))
                            {
                                SetIsDirty(element, false);
                                continue;
                            }
                        }

                        var current = hashCode(child);
                        var ogirinal = GetOriginalData(child);
                        if (!ReferenceEquals(UnsetOriginalData, ogirinal)
                            && ogirinal != null && !ogirinal.Equals(current))
                        {
                            AddToDirtyGroup(child, dirtyGroups);
                            SetIsDirty(child, true);
                            isDirty = true;
                            Debug.WriteLine(string.Format("{0} is dirty, original data [{1}], current [{2}]", child, ogirinal, current));
                            continue;
                            //return true;
                        }

                        SetIsDirty(element, false);
                    }
                    else
                    {
                        if (CheckDirty(child, dirtyGroups))
                        {
                            AddToDirtyGroup(child, dirtyGroups);
                            isDirty = true;
                            continue;
                            //return true;
                        }

                        continue;
                    }
                }
            }

            return isDirty;
        }

        private static void AddToDirtyGroup(DependencyObject element, IList<string> dirtyGroups)
        {
            var dirtyGroup = GetDirtyGroup(element);
            if (dirtyGroup != null)
            {
                if (!dirtyGroups.Contains(dirtyGroup))
                {
                    dirtyGroups.Add(dirtyGroup);
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initialize data for checking dirty purpose.
        /// </summary>
        /// <param name="element">
        /// The element.
        /// </param>
        public static void InitializeOriginalData(DependencyObject element)
        {
            if (element is Visual || element is Visual3D)
            {
                var noVisual = VisualTreeHelper.GetChildrenCount(element);

                for (int i = 0; i < noVisual; i++)
                {
                    var child = VisualTreeHelper.GetChild(element, i);

                    GetValueHashCode hashCode;
                    if (child == null)
                    {
                        continue;
                    }

                    if (Handlers.TryGetValue(child.GetType(), out hashCode))
                    {
                        //var binding = Control.GetBindingExpression(child);
                        //if (binding != null)
                        //{
                        //    binding.UpdateTarget();
                        //}

                        var original = hashCode(child);
                        Debug.WriteLine("Set original data " + original + " " + child);
                        SetOriginalData(child, original);
                        SetIsDirty(child, false);
                        SetDirtyGroupList(element, null);
                        if (!GetIgnore(element))
                        {
                            Action<object> dataChangeHandler;
                            if (DataChangedHandlers.TryGetValue(child.GetType(), out dataChangeHandler))
                            {
                                dataChangeHandler(child);
                            }
                        }
                    }
                    else
                    {
                        InitializeOriginalData(child);
                        continue;
                    }
                }

                SetIsDirty(element, false);
                SetDirtyGroupList(element, null);
            }
        }

        /// <summary>
        /// Initialize data for checking dirty purpose.
        /// </summary>
        /// <param name="element">
        /// The element.
        /// </param>
        public static void ReverseOriginalData(DependencyObject element)
        {
            try
            {
                SetSuppendChecking(element, true);
                InternalReverseOriginalData(element);
                if (element is FrameworkElement)
                {
                    ((FrameworkElement)element).RaiseEvent(new DirtyChangedRoutedEventArgs(DirtyChangedEvent, null, null, DirtyAction.Remove));
                }
            }
            finally
            {
                SetSuppendChecking(element, false);
            }
        }

        /// <summary>
        /// Initialize data for checking dirty purpose.
        /// </summary>
        /// <param name="element">
        /// The element.
        /// </param>
        private static void InternalReverseOriginalData(DependencyObject element)
        {
            if (RevertElement(element))
            {
                return;
            }

            var checker = GetChecker(element);
            if (checker != null)
            {
                if (!checker.Reset())
                {
                    return;
                }
            }

            if (element is Visual || element is Visual3D)
            {
                var noVisual = VisualTreeHelper.GetChildrenCount(element);
                for (int i = 0; i < noVisual; i++)
                {
                    var child = VisualTreeHelper.GetChild(element, i);

                    //var dependencyObject = child;
                    if (child == null)
                    {
                        //ReverseOriginalData(child);
                        //SetIsDirty(child, false);
                        continue;
                    }

                    if (!RevertElement(child))
                    {
                        InternalReverseOriginalData(child);
                        continue;
                    }
                }

                SetIsDirty(element, false);
                SetDirtyGroupList(element, new List<string>());
                if (element is FrameworkElement)
                {
                    if (!GetSuppendChecking(element))
                    {
                        ((FrameworkElement)element).RaiseEvent(
                            new DirtyChangedRoutedEventArgs(DirtyChangedEvent, null, null, DirtyAction.Remove));
                    }
                }
            }
        }

        public static bool RevertElement(DependencyObject element)
        {
            if (GetIgnore(element))
            {
                return true;
            }

            var checker = GetChecker(element);
            if (checker != null)
            {
                if (!checker.Reset())
                {


                    SetIsDirty(element, false);


                    return true;
                }
            }

            Action<object> setter;
            if (SetterHandlers.TryGetValue(element.GetType(), out setter))
            {
                setter(element);
                var ogirinal = GetOriginalData(element);

                GetValueHashCode getter;
                if (Handlers.TryGetValue(element.GetType(), out getter))
                {
                    var current = getter(element);
                    var dirty = false;

                    if (ogirinal != null)
                    {
                        dirty = !ogirinal.Equals(current);
                        if (current is string && !(ogirinal is string))
                        {
                            dirty = !current.ToString().Equals(ogirinal.ToString());
                        }

                        if (dirty)
                        {
                            // Work arround
                            if (element is ComboBox)
                            {
                                var comboBox = ((ComboBox)element);
                                if (comboBox.SelectedValuePath != null && comboBox.SelectedValue != null)
                                {
                                    dirty = !comboBox.SelectedValue.Equals(ogirinal);
                                }
                            }
                        }
                    }

                    SetIsDirty(element, dirty);
                }
                else
                {
                    SetIsDirty(element, false);
                }

                // SetIsDirty(element, false);
                Action<object> dataChangeHandler;

                if (DataChangedHandlers.TryGetValue(element.GetType(), out dataChangeHandler))
                {
                    dataChangeHandler(element);
                }

                return true;
            }

            return false;
        }

        #endregion
    }
}